/**
 * axios 封装
 */
import axios from 'axios'
import { ElMessage } from 'element-plus'
import router from '@/router'
import storage from './storage'
import { useUserStore } from '@/stores/user'

console.log('环境变量', import.meta.env)

// 创建一个 axios 实例
const request = axios.create({
  timeout: 1000 * 3, // 超时时间设置，单位 毫秒
  // TODO. 后续需要处理环境变量，根据不同环境设置不同的基准请求地址（不然，本地开发与上线就得频繁来修改该文件）
  //   当前项目是基于 Vite 的。要去看 Vite 的 环境变量
  //   import.meta.env 可以获取到一些环境变量的对象
  //      1. npm run dev
  //         vite 
  //         import.meta.env.MODE === 'development'  (开发)
  //      2. npm run build
  //         vite build
  //         import.meta.env.MODE === 'production'   (生产)
  //      3. npm run build:stg
  //         vite build --mode stg
  //         import.meta.env.MODE === 'stg'
  //      4. npm run build:uat
  //         vite build --mode uat
  //         import.meta.env.MODE === 'uat'
  //   
  //   于是下面的 baseURL 可以根据当前的 MODE 模式来设置不同的基准地址
  // baseURL: import.meta.env.MODE === 'production' ? 'http://119.91.129.10:8001' : ''
  //   上面主要是处理两个环境的情况，但在公司里面项目可能会有超出2个环境
  // baseURL: import.meta.env.MODE === 'production' ? '生产IP' :
  //          import.meta.env.MODE === 'stg' ? 'STGIP' :
  //          import.meta.env.MODE === 'uat' ? 'UATIP' :
  //          ''
  //   上面的代码，没有问题，但是当环境过多时，这里会显得更加的难以阅读，为了处理该现象，下面的方式才是最终方案
  //    配置环境变量文件的方式
  //    环境变量文件： 在项目根目录下 以 .env.[MODE] 的文件就叫做环境变量文件
  //    环境变量文件的内容如下：（一行就是一个变量）
  //        key1=value1
  //        key2=value2
  //    项目在构建的时候，会根据当前启动的 MODE ，来加载相对应的 环境变量文件，将里面的变量名以 VITE_ 开头的，会添加到 import.meta.env 对象中。

  baseURL: import.meta.env.VITE_API_BASE_URL
})

// 队列
let queq: Array<(newToken: string) => void> = []

// 是否正在刷新token中
let refreshing = false

// 请求拦截
request.interceptors.request.use(
  (config) => {
    const userStore = useUserStore()

    // 获取 token
    const token = storage.get('token')

    if (token) {
      // 写入请求头
      config.headers.Authorization = token

      // 忽略调用 refreshToken 接口的情况
      if (config.url?.includes('refreshToken')) {
        return config
      }

      // 判断token是否过期
      if (storage.isExpired('token')) {
        console.log('token 过期了')

        if (storage.isExpired('refreshToken')) {
          console.log('refreshToken 也过期了，直接退出登录')
          userStore.logout()
        } else {
          if (!refreshing) {
            refreshing = true
            // 调用更新token的接口
            userStore.refreshToken().then((resp) => {
              console.log('刷新token成功，数据为', resp)

              // 1. 重新设置一下token
              userStore.setToken(resp.data, true)

              // 2. 将队列中的函数一个一个取出来执行
              console.log(queq.length, '队列长度')
              queq.forEach((cb) => cb(resp.data.token))

              // 3. 将 refreshing 重置为 false
              refreshing = false

              // 4. 将 队列清空
              queq = []
            })
          }

          // 返回一个新的 Promise, 让当前的接口不发出去。
          return new Promise((resolve) => {
            // 将当前请求追加到队列中，等到后续再发出
            queq.push((newToken) => {
              config.headers.Authorization = newToken
              resolve(config)
            })
          })
        }
      }
    }

    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)

// 响应拦截
request.interceptors.response.use(
  (response) => {
    // data - 接口返回的数据
    const { data } = response

    // code - 业务状态码，message - 业务消息描述
    const { code, message } = data
    // 判断业务状态码，不同的状态码处理不同的逻辑
    switch (code) {
      case 1000:
        // ElMessage({
        //   message,
        //   type: 'success'
        // })
        // 成功，直接返回 data, 让业务代码上可以少处理一层 data
        return data

      default:
        // 做弹窗提示
        ElMessage({
          message,
          type: 'error'
        })
        return Promise.reject(data)
    }
  },
  (error) => {
    // console.log('响应拦截 error', error)
    // 解构出 http 状态码
    const {
      response: { status, statusText }
    } = error

    switch (status) {
      case 401:
        // 没有权限，没有登录
        ElMessage.error('登录失效或无权限访问~')
        // TODO. 可以主动将路由跳转到登录页面
        router.replace('/login')
        return

      default:
        // 默认，弹出一个错误
        ElMessage.error(statusText)
        break
    }
    return Promise.reject(error)
  }
)

// 最后，导出该实例
export default request
